# -*- coding: utf-8 -*-
import base64
import hashlib
import json
import xml.etree.ElementTree as ET
from collections import OrderedDict
from copy import copy
from random import random

import requests
from django.conf import settings
from pyDes import des, CBC, PAD_PKCS5

from async import async_job
from common.cache import redis_cache
from common.channel.pay import check_channel_order
from common.order import db as order_db
from common.order.model import PAY_STATUS
from common.utils import track_logging
from common.utils.exceptions import ParamError
from common.utils.ip_address import check_valid_ip_address

_LOGGER = track_logging.getLogger(__name__)

APP_CONF = {
    # 'UE8945': {  # 二代存款支付 云闪付 1.6% 50~5000 dwc
    #     'API_KEY': 'd8a8eacfb3b1416a8d1aad9499494192',
    #     'key1': '4xudoCgRCJc=',
    #     'key2': 'LtI22bWGx4M=',
    #     'gateway': 'https://dp.pr0pay.com/webapi/PMToService2.aspx',
    #     'gateway': 'https://dp.pr0pay.com/webapi/ToService.aspx',
    #     'query_gateway': 'https://dp.pr0pay.com/webapi/query',
    # },
    'UE8943': {  # 二代存款支付 银联快捷 1.7% 10~5000 dwc
        'API_KEY': '1e38f65dd38a4b4fac3c39593b0227b6',
        'key1': 'es9pb3X+Meo=',
        'key2': 'WKs7aJ8MNOM=',
        'gateway': 'https://dp.pr0pay.com/webapi/PMToService2.aspx',
        'query_gateway': 'https://dp.pr0pay.com/webapi/query',
    },
}


def _get_api_key(mch_id):
    return APP_CONF[mch_id]['API_KEY']


def _get_key1(mch_id):
    return APP_CONF[mch_id]['key1']


def _get_key2(mch_id):
    return APP_CONF[mch_id]['key2']


def _get_gateway(mch_id):
    return APP_CONF[mch_id]['gateway']


def _get_query_gateway(mch_id):
    return APP_CONF[mch_id]['query_gateway']


def _md5(s):
    m = hashlib.md5()
    m.update(s.encode('utf8'))
    return m.hexdigest().lower()


def verify_data(md5_data, xml_data, key):
    calculated_data = _md5(xml_data + key)
    if md5_data != calculated_data:
        _LOGGER.info("sdppay sign: %s, calculated sign: %s", md5_data, calculated_data)
        raise ParamError('sign not pass, data: %s' % xml_data)


def verify_query_data(md5_data, xml_data):
    calculated_data = _md5(xml_data)
    if md5_data != calculated_data:
        _LOGGER.info("sdppay sign: %s, calculated sign: %s", md5_data, calculated_data)
        raise ParamError('sign not pass, data: %s' % xml_data)


def _build_form(params, gateway):
    html = u"<head><title>loading...</title></head><form id='submit' name='submit' action='" + gateway + "' method='post'>"
    for k, v in params.items():
        html += "<input type='hidden' name='%s' value='%s'/>" % (k, v)
    html += "</form>"
    html += "<script>doc" \
            "ument.forms['submit'].submit();</script>"
    return html


def des_encrypt(s, key, iv_key):
    """
    DES 加密
    :param s: 原始字符串
    :return: 加密后字符串，16进制
    """
    secret_key = base64.b64decode(key)
    iv = base64.b64decode(iv_key)
    k = des(secret_key, CBC, iv, pad=None, padmode=PAD_PKCS5)
    en = k.encrypt(s, padmode=PAD_PKCS5)
    return base64.b64encode(en)


def des_descrypt(s, key, iv_key):
    """
    DES 解密
    :param s: 加密后的字符串，16进制
    :return:  解密后的字符串
    """
    secret_key = base64.b64decode(key)
    iv = base64.b64decode(iv_key)
    k = des(secret_key, CBC, iv, pad=None, padmode=PAD_PKCS5)
    de = k.decrypt(base64.b64decode(s), padmode=PAD_PKCS5)
    return de


def create_charge(pay, pay_amount, info):
    ''' 创建订单 '''
    app_id = info['app_id']
    api_key = _get_api_key(app_id)
    XML_DATA = "<message>" \
               "<cmd>6010</cmd>" \
               "<merchantid>{}</merchantid>" \
               "<language>zh-cn</language>" \
               "<userinfo>" \
               "<order>{}</order>" \
               "<username>{}</username>" \
               "<money>{}</money>" \
               "<unit>1</unit>" \
               "<bank>BOC</bank>" \
               "<time></time>" \
               "<remark></remark>" \
               "<backurl>{}</backurl>" \
               "<backurlbrowser></backurlbrowser>" \
               "</userinfo>" \
               "</message>".format(app_id,
                                   str(pay.id),
                                   str(pay.user_id),
                                   '%.2f' % pay_amount,
                                   '{}/pay/api/{}/sdppay/{}'.format(
                                       settings.NOTIFY_PREFIX,
                                       settings.NOTIFY_PATH, app_id)
                                   )
    _LOGGER.info("sdppay create data: %s, order_id is: %s", XML_DATA,
                 pay.id)
    MD5_ENCRYPT = _md5(XML_DATA + api_key)
    RANDOM_VALUE = random()
    DATA_TO_ENCRYPT = XML_DATA + MD5_ENCRYPT + _md5(str(RANDOM_VALUE))

    _LOGGER.info("DATA_TO_ENCRYPT is: %s", DATA_TO_ENCRYPT)

    DES_DATA = des_encrypt(DATA_TO_ENCRYPT, _get_key1(app_id), _get_key2(app_id))

    _LOGGER.info("DES_DATA is: %s", DES_DATA)

    parameter_dict = OrderedDict((
        ('pid', app_id),
        ('des', DES_DATA),
    ))
    _LOGGER.info("parameter_dict is: %s", json.dumps(parameter_dict))
    html_text = _build_form(parameter_dict, _get_gateway(app_id))
    cache_id = redis_cache.save_html(pay.id, html_text)
    _LOGGER.info('sdppay url: %s', settings.PAY_CACHE_URL + cache_id)
    return {'charge_info': settings.PAY_CACHE_URL + cache_id}


def dictify(r, root=True):
    if root:
        return {r.tag: dictify(r, False)}
    d = copy(r.attrib)
    if r.text:
        d["_text"] = r.text
    for x in r.findall("./*"):
        if x.tag not in d:
            d[x.tag] = []
        d[x.tag].append(dictify(x, False))
    return d


# SUCCESS
def check_notify_sign(request, app_id):
    data_origin = dict(request.POST.iteritems())
    api_key = _get_api_key(app_id)
    _LOGGER.info("sdppay notify data: %s", data_origin)
    md5_data = des_descrypt(data_origin['res'], _get_key1(app_id), _get_key2(app_id))[-64:-32]
    xml_data = des_descrypt(data_origin['res'], _get_key1(app_id), _get_key2(app_id))[:-64]

    verify_data(md5_data, xml_data, api_key)
    root = ET.fromstring(xml_data)
    data = dictify(root)['message']
    pay_id = data['order'][0]['_text']
    check_valid_ip_address(str(request.META['REMOTE_ADDR']), pay_id)
    if not pay_id:
        _LOGGER.error("fatal error, out_trade_no not exists, data: %s", data)
        raise ParamError('sdppay event does not contain pay ID')

    pay = order_db.get_pay(pay_id)
    if not pay:
        raise ParamError('pay_id: %s invalid' % pay_id)
    if pay.status != PAY_STATUS.READY:
        raise ParamError('pay %s has been processed' % pay_id)

    mch_id = pay.mch_id
    trade_status = str(data['result'][0]['_text'])
    trade_no = ''
    total_fee = float(data['money'][0]['_text'])

    extend = {
        'trade_status': trade_status,
        'trade_no': trade_no,
        'total_fee': total_fee
    }
    check_channel_order(pay_id, total_fee, app_id)
    # 等待: 0, 完成: 1, 失败: 2
    if trade_status == '1':
        _LOGGER.info('sdppay check order success, mch_id:%s pay_id:%s' % (mch_id, pay_id))
        order_db.add_pay_success(mch_id, pay_id, total_fee, trade_no, extend)
        # async notify
        async_job.notify_mch(pay_id)


def query_charge(pay_order, app_id):
    ''' 查询订单 '''
    pay_id = pay_order.id
    api_key = _get_api_key(app_id)
    XML_DATA = "<message>" \
               "<cmd>100011</cmd>" \
               "<merchantId>{}</merchantId>" \
               "<order>{}</order>" \
               "</message>".format(app_id,
                                   str(pay_id)
                                   )
    _LOGGER.info("sdppay create data: %s, order_id is: %s", XML_DATA,
                 pay_order.id)
    MD5_ENCRYPT = _md5(XML_DATA + api_key)
    RANDOM_VALUE = random()
    DATA_TO_ENCRYPT = XML_DATA + MD5_ENCRYPT + _md5(str(RANDOM_VALUE))
    DES_DATA = des_encrypt(DATA_TO_ENCRYPT, _get_key1(app_id), _get_key2(app_id))

    parameter_dict = OrderedDict((
        ('pid', app_id),
        ('res', DES_DATA),
    ))
    _LOGGER.info("sdppay query parameter_dict is: %s", json.dumps(parameter_dict))
    headers = {'Content-Type': 'application/x-www-form-urlencoded'}
    response = requests.post(_get_query_gateway(app_id), data=parameter_dict, headers=headers, timeout=3)
    _LOGGER.info('sdppay create response: %s:', response.text)
    data_origin = str(response.text).split("=")[1]
    md5_data = des_descrypt(data_origin, _get_key1(app_id), _get_key2(app_id))[-64:-32]
    xml_data = des_descrypt(data_origin, _get_key1(app_id), _get_key2(app_id))[:-64]

    verify_query_data(md5_data, xml_data)
    root = ET.fromstring(xml_data)
    data = dictify(root)['message']
    pay_id = data['reforder'][0]['_text']
    if response.status_code == 200:
        trade_status = str(data['state'][0]['_text'])
        trade_no = str(data['order'][0]['_text'])
        total_fee = float(data['money'][0]['_text'])
        extend = {
            'trade_status': trade_status,
            'trade_no': trade_no,
            'total_fee': total_fee
        }
        # 等待: 0, 成功: 1, 失败: 2
        if trade_status == '1':
            _LOGGER.info('sdppay query order success, user_id:%s pay_id:%s' % (pay_order.mch_id, pay_id))
            order_db.add_pay_success(pay_order.mch_id, pay_order.id,
                                     total_fee, trade_no, extend)
            async_job.notify_mch(pay_order.id)
    else:
        _LOGGER.warn('sdppay data error, status_code: %s', response.status_code)
